package abio.XasML.builder
{
	import abio.XasML.builder.XasML.assemble;
	import abio.XasML.builder.XasML.declear;
	import abio.XasML.tag.implAssembleTag_method;
	import abio.XasML.tag.implAssembleTag_property;
	
	import org.spicefactory.lib.reflect.ClassInfo;
	import org.spicefactory.lib.reflect.Method;
	import org.spicefactory.lib.reflect.Parameter;
	import org.spicefactory.lib.reflect.Property;
	import org.spicefactory.lib.util.ClassUtil;
	
	internal class objectAssemble
	{
		public function objectAssemble()
		{
		}
		
		static private function _getDeclearByID(id:String, cntxt:context):declear{
			for(var tempContext:context=cntxt; tempContext!=null; tempContext=tempContext.Parent){
				if((tempContext.Builder.hasOwnProperty(id)) && (tempContext.Builder[id] is declear)){
					return tempContext.Builder[id] as declear;
				}
			}
			return null;
		}		
		static private function _getDeclearByType(type:Class, cntxt:context):declear{
			var result:declear=null;
			for each(var tempProperty:Property in ClassInfo.forInstance(cntxt.Builder).getProperties()){
				if(tempProperty.getValue(cntxt.Builder) is declear
					&& (tempProperty.getValue(cntxt.Builder) as declear).type is Class
					&& ClassInfo.forClass((tempProperty.getValue(cntxt.Builder) as declear).type as Class).isType(type)){
					if(result==null){
						result=tempProperty.getValue(cntxt.Builder) as declear;
					}else{
						throw('objectAssemble type conflicted');
					}
				}
			}
			return result;
		}		
		static private function _getDeclear(type:Object, cntxt:context):declear{
			if(type is String){
				return objectAssemble._getDeclearByID(type as String, cntxt);
			}else if(type is Class){
				return objectAssemble._getDeclearByType(type as Class, cntxt);
			}else{
				return null;
			}
		}

		static private function _createInstance(Declear:declear, cntxt:context):Object{
			if(Declear.type is Class){
				return ClassUtil.createNewInstance(Declear.type as Class, Declear.constructorArgs);
			}else if(Declear.type is Function){
				var tempFunc:Function=Declear.type as Function;
				return tempFunc.apply(null, Declear.constructorArgs);
			}else{
				return Declear.type;
			}
		}
		
		static private function _assembleInstanceProperty(obj:Object, Declear:declear, cntxt:context):Object{
			for each(var property:Property in ClassInfo.forInstance(obj).getProperties()){
				if(property.getMetadata(implAssembleTag_property).length>0){
					var bValueSetted:Boolean=false;
					for each(var tempAssemble:assemble in Declear.assembleSet){
						if(tempAssemble.name==property.name && tempAssemble.parameter.length>0){
							if(bValueSetted==false){
								property.setValue(obj, cntxt.newInstance(tempAssemble.parameter[0]));
								bValueSetted=true;
							}else{
								throw('objectAssemble name conflicted');
							}
						}
					}
					if(bValueSetted==false){
						property.setValue(obj, cntxt.newInstance(property.type.getClass()));
					}
				}
			}
			return obj;
		}		
		static private function _assembleInstanceMethod(obj:Object, Declear:declear, cntxt:context):Object{
			for each(var method:Method in ClassInfo.forInstance(obj).getMethods()){
				if(method.getMetadata(implAssembleTag_method).length>0){
					var bInvoked:Boolean=false;
					for each(var tempAssemble:assemble in Declear.assembleSet){
						if(tempAssemble.name==method.name){
							if(bInvoked==false){
								var paras:Array=new Array();
								for(var i:int=0; i<method.parameters.length; i++)
								{
									if(tempAssemble.parameter[i]){
										paras.push(cntxt.newInstance(tempAssemble.parameter[i]));
									}else if((method.parameters[i] as Parameter).required){
										paras.push(cntxt.newInstance((method.parameters[i] as Parameter).type.getClass()));
									}
								}
								method.invoke(obj, paras);
								bInvoked=true;
							}else{
								throw('objectAssemble name conflicted');
							}
						}
					}
				}
			}
			return obj;
		}		
		static private function _assembleInstance(obj:Object, Declear:declear, cntxt:context):Object{
			objectAssemble._assembleInstanceProperty(obj, Declear, cntxt);
			objectAssemble._assembleInstanceMethod(obj, Declear, cntxt);
			return obj;
		}
		
		static internal function newInstance(type:Object, cntxt:context):Object{
			var Declear:declear=objectAssemble._getDeclear(type, cntxt);			
			var result:Object=objectAssemble._createInstance(Declear, cntxt);
			return objectAssemble._assembleInstance(result, Declear, cntxt);
		}
	}
}